/*
============================================================================
 Name        : StreamWriter.cpp
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : CStreamWriter implementation
============================================================================
*/

#include "StreamWriter.h"
#include "StreamBuffer.h"
#include "PluginUtility.h"

#define BUFFER_READ_SIZE 8192
#define BUFFER_READ_START 81920

EXPORT_C CStreamWriter::CStreamWriter(MStreamBufferObserver& aObserver)
: CActive(EPriorityStandard), iObserver(aObserver), iStreamBuffer(NULL), iWritingToStream(EFalse), iVolume(0),
iBufferToRead(NULL), iBufferToReadPtr(NULL, 0, 0), iIsBuffering(EFalse), iAvailableBytes(0), iState(ENotReady)
{
}

EXPORT_C CStreamWriter::~CStreamWriter()
{
	Cancel();
	CloseAndClean();
}

EXPORT_C CStreamWriter* CStreamWriter::NewLC(MStreamBufferObserver& aObserver)
{
	CStreamWriter* self = new (ELeave)CStreamWriter(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL();
	return self;
}

EXPORT_C CStreamWriter* CStreamWriter::NewL(MStreamBufferObserver& aObserver)
{
	CStreamWriter* self = CStreamWriter::NewLC(aObserver);
	CleanupStack::Pop(); // self;
	return self;
}

EXPORT_C void CStreamWriter::ConstructL()
{
	iDataType.Set(KMMFFourCCCodeMP3);

	//For bitrate > 64000
	iAudioSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate44100Hz;
	iAudioSettings.iChannels = TMdaAudioDataSettings::EChannelsStereo;

	iStreamOutput = CMdaAudioOutputStream::NewL(*this);
	iStreamOutput->SetPriority(80 , EMdaPriorityPreferenceTimeAndQuality);
	iStreamOutput->Open(&iSettings);

	iVolumeStep = iStreamOutput->MaxVolume() / 10;

	//By default, volume 4
	SetVolume(4);

	//allocate the reading buffer
	iBufferToRead = new (ELeave) TUint8[BUFFER_READ_SIZE];
	iBufferToReadPtr.Set(iBufferToRead, 0, BUFFER_READ_SIZE);

	//Initialize timer
	User::LeaveIfError(iTimer.CreateLocal());	

	//Add to scheduler
	CActiveScheduler::Add(this);
}

EXPORT_C void CStreamWriter::StartL()
{
	iState = EBuffering;
	iTimer.After(iStatus, TTimeIntervalMicroSeconds32(1000000));

	//Reset buffering variable
	SetBufferingState(ETrue, 0);

	SetActive();
}

EXPORT_C TInt CStreamWriter::Volume()
{
	return iVolume;
}

EXPORT_C void CStreamWriter::SetVolume(TInt aVolume)
{
	iVolume = aVolume;
	iStreamOutput->SetVolume(iVolume * iVolumeStep);
}

EXPORT_C void CStreamWriter::RunL()
{
	switch (iState)
	{
	case EBuffering:
		//data have been read. Try to write on buffer
		if (iStatus == KErrNone)
		{
			ReadToBuffer();
		}
		break;

	case EFinished:
		//Finish of process
		Finished();
		return;
	}

	if (iState == EFailed)
	{
		Cancel();
	}
	else
	{
		iTimer.After(iStatus, 10);
		SetActive();
	}
}

void CStreamWriter::ReadToBuffer()
{
	if (iWritingToStream) return;

	TInt availableBytes = iStreamBuffer->GetBytesReadyToRead();
	if (availableBytes == 0)
	{
		if (iStreamBuffer->IsFinished()) iState = EFinished;
		return;
	}

	if (iIsBuffering)
	{
		//Check if we have enough data
		if (!iStreamBuffer->IsFinished() && availableBytes < BUFFER_READ_START)
		{
			//Change state
			SetBufferingState(ETrue, availableBytes);

			return;
		}

		//Change state
		SetBufferingState(EFalse, -1);
	}
	else
	{
		if (!iStreamBuffer->IsFinished() && availableBytes <= BUFFER_READ_SIZE)
		{
			//Change state
			SetBufferingState(ETrue, availableBytes);

			return;
		}
	}

	//We have enough data to play
	iWritingToStream = ETrue;

	//Read in buffer
	iBufferToReadPtr.Zero();
	iStreamBuffer->Read(&iBufferToReadPtr, BUFFER_READ_SIZE > availableBytes ? availableBytes : BUFFER_READ_SIZE);

	//Write in stream output
	iStreamOutput->WriteL(iBufferToReadPtr);
}

void CStreamWriter::Finished()
{
	Cancel();
	iObserver.Finished();
}

void CStreamWriter::SetStreamBuffer(CStreamBuffer* aStreamBuffer)
{
	iStreamBuffer = aStreamBuffer;
	iStreamBuffer->SetWriter(this);
}

EXPORT_C void CStreamWriter::DoCancel()
{
	iTimer.Cancel();
}

void CStreamWriter::CloseAndClean()
{
	iTimer.Close();
	
	DELETE(iStreamOutput);
	
	//delete buffer to read
	iBufferToReadPtr.Set(NULL, 0, 0);
	DELETE(iBufferToRead);
}

void CStreamWriter::SetBufferingState(TBool aIsBuffering, TInt aAvailable)
{
	if (iIsBuffering == aIsBuffering && iAvailableBytes == aAvailable) return;

	iIsBuffering = aIsBuffering;
	iAvailableBytes = aAvailable;

	//Calculate percent
	TInt percent = (100 * aAvailable) / BUFFER_READ_START;

	//Notify observer
	iObserver.BufferStateChanged(iIsBuffering, percent);

}

EXPORT_C void CStreamWriter::MaoscOpenComplete(TInt aError)
{
	if ( aError != KErrNone )
	{
		return;
	}

	// If this leaves with KErrNotSupported, no decoder was found that support the
	// specified encoded data stream.
	TRAPD(err, iStreamOutput->SetDataTypeL(iDataType.FourCC()));
	if ( err )
	{
		return;
	}

	// [Petteri] This needs to be set after the data type, at least on
	// 9500 and 6630, otherwise the output will be 8kHz mono or something
	// similar
	TRAP(err, iStreamOutput->SetAudioPropertiesL(iAudioSettings.iSampleRate, iAudioSettings.iChannels));
	if ( err )
	{
		return;
	}
}

EXPORT_C void CStreamWriter::MaoscBufferCopied(TInt aError, const TDesC8& /*aBuffer*/) 
{
	if (aError != KErrNone)
	{
		iState = EFailed;
	}
	iWritingToStream = EFalse;
}

EXPORT_C void CStreamWriter::MaoscPlayComplete(TInt aError)
{
	iState = EFailed;
}